﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Linq.Expressions;
using System.Web.Routing;
using System.Xml.Linq;

namespace Toolkit.Web.Mvc.ExtensionMethods
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Get the dynamically generated name attribute value for a given expression.
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="htmlHelper"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string NameFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            return htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression));
        }

        /// <summary>
        /// Get the dynamically generated id attribute value for a given expression.
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="htmlHelper"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string IdFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            return htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
        }

        #region Custom DropDownList Extensions
        /// <summary>
        /// Custom dropdown list extension method. Allows attributes on option tags.
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="name"></param>
        /// <param name="selectList"></param>
        /// <returns></returns>
        public static MvcHtmlString CustomDropDownListFor(this HtmlHelper htmlHelper, string name, IEnumerable<CustomSelectListItem> selectList)
        {
            return CustomDropDownListHelper(htmlHelper, htmlHelper.DropDownList(name, (IEnumerable<SelectListItem>)selectList), selectList);
        }

        /// <summary>
        /// Custom dropdown list extension method. Allows attributes on option tags.
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="name"></param>
        /// <param name="selectList"></param>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        public static MvcHtmlString CustomDropDownListFor(this HtmlHelper htmlHelper, string name, IEnumerable<CustomSelectListItem> selectList, object htmlAttributes)
        {
            return CustomDropDownListHelper(htmlHelper, htmlHelper.DropDownList(name, (IEnumerable<SelectListItem>)selectList, htmlAttributes), selectList);
        }

        /// <summary>
        /// Custom dropdown list extension method. Allows attributes on option tags.
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="htmlHelper"></param>
        /// <param name="expression"></param>
        /// <param name="selectList"></param>
        /// <returns></returns>
        public static MvcHtmlString CustomDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<CustomSelectListItem> selectList)
        {
            return CustomDropDownListFor<TModel, TProperty>(htmlHelper, expression, selectList, null);
        }

        /// <summary>
        /// Custom dropdown list extension method. Allows attributes on option tags.
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="htmlHelper"></param>
        /// <param name="expression"></param>
        /// <param name="selectList"></param>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        public static MvcHtmlString CustomDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<CustomSelectListItem> selectList, object htmlAttributes)
        {
            return CustomDropDownListHelper(htmlHelper, htmlHelper.DropDownListFor<TModel, TProperty>(expression, (IEnumerable<SelectListItem>)selectList, htmlAttributes), selectList);
        }

        /// <summary>
        /// Custom dropdown list extension method. Allows attributes on option tags.
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="markup"></param>
        /// <param name="selectList"></param>
        /// <returns></returns>
        private static MvcHtmlString CustomDropDownListHelper(this HtmlHelper htmlHelper, MvcHtmlString markup, IEnumerable<CustomSelectListItem> selectList)
        {
            XDocument selectDoc = XDocument.Parse(markup.ToString());
            IEnumerable<XElement> options = from XElement el in selectDoc.Element("select").Descendants() select el;

            int index = 0;
            foreach (XElement item in options)
            {
                XAttribute itemValue = item.Attribute("value");
                CustomSelectListItem customSelectListItem = selectList.ElementAt(index);
                if (customSelectListItem.HtmlAttributes != null)
                {
                    foreach (KeyValuePair<string, object> attribute in customSelectListItem.HtmlAttributes)
                    {
                        if (!attribute.Key.Equals("selected") && !attribute.Key.Equals("value") && !attribute.Key.Equals("id"))
                        {
                            item.SetAttributeValue(attribute.Key, attribute.Value);
                        }
                    }
                }
                index += 1;
            }

            return MvcHtmlString.Create(selectDoc.ToString());
        }
        #endregion
    }

    /// <summary>
    /// Extension of the SelectListItem class which provides access to an html attribute collection. This is used by the CustomDropDownList
    /// extension methods.
    /// </summary>
    public class CustomSelectListItem : SelectListItem
    {
        public CustomSelectListItem()
            : base()
        {
            HtmlAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        }

        public CustomSelectListItem(object htmlAttributes)
            : base()
        {
            HtmlAttributes = new RouteValueDictionary(htmlAttributes);
        }

        public IDictionary<string, object> HtmlAttributes
        {
            get;
            set;
        }
    }
}
